package ai.koog.agents.core.feature.model.events

import ai.koog.agents.core.annotation.InternalAgentsApi
import ai.koog.agents.core.feature.model.AIAgentError
import ai.koog.agents.core.utils.SerializationUtils
import kotlinx.datetime.Clock
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement

/**
 * Represents an event triggered when the execution of a specific AI agent node starts.
 *
 * This event captures the initial execution context of an AI agent's processing node.
 * It provides information about the node's name, the input being processed,
 * and a unique identifier for the event.
 *
 * Events of this kind are part of the feature event system and are primarily
 * used for tracking and monitoring purposes, particularly in scenarios where
 * understanding the flow of agent execution is essential.
 *
 * @property nodeName The name of the node whose execution is starting;
 * @property input The input data provided to the node and serialized into JSON element;
 * @property timestamp The timestamp of the event, in milliseconds since the Unix epoch.
 */
@Serializable
public data class NodeExecutionStartingEvent(
    val runId: String,
    val nodeName: String,
    val input: JsonElement?,
    override val timestamp: Long = Clock.System.now().toEpochMilliseconds(),
) : DefinedFeatureEvent() {

    /**
     * Creates an instance of [NodeExecutionStartingEvent].
     *
     * This constructor is deprecated and should be replaced with the constructor
     * that accepts an input parameter of type [JsonElement].
     */
    @Deprecated("Use constructor with input parameter of type [JsonElement]")
    public constructor(
        runId: String,
        nodeName: String,
        input: String,
        timestamp: Long = Clock.System.now().toEpochMilliseconds()
    ) : this(
        runId = runId,
        nodeName = nodeName,
        input = @OptIn(InternalAgentsApi::class)
        SerializationUtils.parseDataToJsonElementOrDefault(input),
        timestamp = timestamp
    )
}

/**
 * Represents an event indicating the completion of a node's execution within an AI agent.
 *
 * This event is triggered when a specific processing node, identified by its name,
 * concludes its execution. It encapsulates details about the node's name,
 * the input it operated on, and the output it produced.
 *
 * @property nodeName The name of the node that finished execution;
 * @property input The input data provided to the node;
 * @property output The output generated by the node;
 * @property timestamp The timestamp of the event, in milliseconds since the Unix epoch.
 */
@Serializable
public data class NodeExecutionCompletedEvent(
    val runId: String,
    val nodeName: String,
    val input: JsonElement?,
    val output: JsonElement?,
    override val timestamp: Long = Clock.System.now().toEpochMilliseconds(),
) : DefinedFeatureEvent() {

    /**
     * Creates an instance of [NodeExecutionCompletedEvent].
     *
     * This constructor is deprecated and should be replaced with the constructor
     * that accepts input and output parameters of type [JsonElement].
     */
    @Deprecated("Use constructor with input and output parameters of type [JsonElement]")
    public constructor(
        runId: String,
        nodeName: String,
        input: String,
        output: String,
        timestamp: Long = Clock.System.now().toEpochMilliseconds()
    ) : this(
        runId = runId,
        nodeName = nodeName,
        input = @OptIn(InternalAgentsApi::class)
        SerializationUtils.parseDataToJsonElementOrDefault(input),
        output = @OptIn(InternalAgentsApi::class)
        SerializationUtils.parseDataToJsonElementOrDefault(output),
        timestamp = timestamp
    )
}

/**
 * Represents an event that signifies the occurrence of an error during the execution of a specific node
 * within an AI agent's process.
 *
 * @property runId A unique identifier associated with the specific run of the AI agent;
 * @property nodeName The name of the node where the error occurred;
 * @property input The input data provided to the node;
 * @property error An instance of `AIAgentError` containing the error details, such as a message, stack trace, and optional cause;
 * @property timestamp The timestamp of the event, in milliseconds since the Unix epoch.
 */
@Serializable
public data class NodeExecutionFailedEvent(
    val runId: String,
    val nodeName: String,
    val input: JsonElement?,
    val error: AIAgentError,
    override val timestamp: Long = Clock.System.now().toEpochMilliseconds(),
) : DefinedFeatureEvent() {

    /**
     * Creates an instance of [NodeExecutionFailedEvent].
     *
     * This constructor is deprecated and should be replaced with the constructor
     * that accepts an input parameter of type [JsonElement].
     */
    @Deprecated("Use constructor with input parameter of type [JsonElement]")
    public constructor(
        runId: String,
        nodeName: String,
        error: AIAgentError,
        timestamp: Long = Clock.System.now().toEpochMilliseconds()
    ) : this(
        runId = runId,
        nodeName = nodeName,
        input = null,
        error = error,
        timestamp = timestamp
    )
}

//region Deprecated

@Deprecated(
    message = "Use NodeExecutionStartingEvent instead",
    replaceWith = ReplaceWith("NodeExecutionStartingEvent")
)
public typealias AIAgentNodeExecutionStartEvent = NodeExecutionStartingEvent

@Deprecated(
    message = "Use NodeExecutionCompletedEvent instead",
    replaceWith = ReplaceWith("NodeExecutionCompletedEvent")
)
public typealias AIAgentNodeExecutionEndEvent = NodeExecutionCompletedEvent

@Deprecated(
    message = "Use NodeExecutionFailedEvent instead",
    replaceWith = ReplaceWith("NodeExecutionFailedEvent")
)
public typealias AIAgentNodeExecutionErrorEvent = NodeExecutionFailedEvent

//endregion Deprecated
